package main

//https://github.com/elazarl/go-bindata-assetfs
//go get github.com/go-bindata/go-bindata/...
//go get github.com/elazarl/go-bindata-assetfs/...
// go-bindata-assetfs html/...

import (
	"bytes"
	"encoding/json"
	"fmt"
	assetfs "github.com/elazarl/go-bindata-assetfs"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
)

type Handle struct {
	fs  http.Handler
	cfg *Service
}
type Service struct {
	Prefixs []string
	Host    string
	Doc     string
}

func (this *Handle) CallApi(w http.ResponseWriter, r *http.Request) bool {
	needProxy := false
	for _, prifix := range this.cfg.Prefixs {
		if len(r.URL.Path) > len(prifix) && r.URL.Path[:len(prifix)] == prifix {
			needProxy = true
			break
		}
	}
	if !needProxy {
		return false
	}

	fmt.Printf("Received request %s %s %s\n", r.Method, r.Host, r.RemoteAddr)

	//r.host不带http,r.url是完整的url
	//fmt.Println(r.Host, " ", r.URL, "\n")

	fmt.Println("url: ", r.URL)

	//查看url各个信息
	// str := "hi ,it is working"
	// b := []byte(str)
	//w.Write(b)
	//fmt.Print(r.Host, " ", r.Method, " \nr.URL.String ", r.URL.String(), " r.URL.Host ", r.URL.Host, " r.URL.Fragment ", r.URL.Fragment, " r.URL.Hostname ", r.URL.Hostname(), " r.URL.RequestURI ", r.URL.RequestURI(), " r.URL.Scheme ", r.URL.Scheme)

	cli := &http.Client{}

	//不建议用readfull，对于body大小难以判断，容易出错
	// body := make([]byte, 2048000)
	// n, err := io.ReadFull(r.Body, body)

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Print("io.ReadFull(r.Body, body) ", err.Error())
		//return,没有数据也是可以的，不需要直接结束
	}
	fmt.Print("req count :", len(body), "\n")

	//fmt.Print(len(body))
	//reqUrl := r.Host + r.URL.String()

	//reqUrl := r.URL.String()

	//url里带了协议类型，不需要用scheme
	// if r.URL.Scheme != "" {
	//     reqUrl = r.URL.Scheme + reqUrl
	// } else {
	//     reqUrl = "http://" + reqUrl
	// }

	req, err := http.NewRequest(r.Method, this.cfg.Host+r.URL.Path, strings.NewReader(string(body)))
	if err != nil {
		fmt.Print("http.NewRequest ", err.Error())
		return true
	}

	//用遍历header实现完整复制
	//contentType := r.Header.Get("Content-Type")
	//req.Header.Set("Content-Type", contentType)

	for k, v := range r.Header {
		req.Header.Set(k, v[0])
	}
	res, err := cli.Do(req)
	if err != nil {
		fmt.Print("cli.Do(req) ", err.Error())
		return true
	}
	defer res.Body.Close()

	// n, err = io.ReadFull(res.Body, body)
	// if err != nil {
	//     fmt.Print("io.ReadFull(res.Body, body) ", err.Error())
	//     return
	// }
	//fmt.Print("count body bytes: ", n, "\n")

	for k, v := range res.Header {
		w.Header().Set(k, v[0])
	}
	io.Copy(w, res.Body)
	return true
}

func (this *Handle) GetDoc(resp http.ResponseWriter, r *http.Request) {

	docresp, e := http.Get(this.cfg.Doc)
	if e != nil {
		return
	}
	b, e := ioutil.ReadAll(docresp.Body)
	if e != nil {
		return
	}
	resp.Header().Add("Content-Type", "application/json;charset=UTF-8")
	resp.Write(b)

}
func (this *Handle) GetConfig(resp http.ResponseWriter, r *http.Request) {

	resp.Header().Add("Content-Type", "application/json;charset=UTF-8")
	d, _ := json.Marshal(this.cfg)
	resp.Write(d)

}
func (this *Handle) SetConfig(resp http.ResponseWriter, r *http.Request) {

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Printf("read body err, %v\n", err)
		return
	}

	json.Unmarshal(body, this.cfg)
	for i := 0; i < len(this.cfg.Prefixs); i++ {
		if this.cfg.Prefixs[i][:1] != "/" {
			this.cfg.Prefixs[i] = "/" + this.cfg.Prefixs[i]
		}
	}
	save(this.cfg)
	println("json:", string(body))
}

func (this *Handle) Do(resp http.ResponseWriter, req *http.Request) bool {
	if req.URL.Path == "/s_setconfig" {
		this.SetConfig(resp, req)
		return true
	}
	if req.URL.Path == "/s_getconfig" {
		this.GetConfig(resp, req)
		return true
	}
	if req.URL.Path == "/json/swagger.json" {
		this.GetDoc(resp, req)
		return true
	}
	if this.CallApi(resp, req) {
		return true
	}

	return false
}
func (this *Handle) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	if !this.Do(resp, req) {
		this.fs.ServeHTTP(resp, req)
	}
}

func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
func save(svrcfg *Service) {
	dd, _ := json.Marshal(svrcfg)
	var out bytes.Buffer
	json.Indent(&out, dd, "", "\t")
	ioutil.WriteFile("./c.json", out.Bytes(), os.ModePerm)
}

func main() {

	ex, _ := PathExists("./c.json")
	var svrcfg *Service = nil

	if ex {
		b, e := ioutil.ReadFile("./c.json")
		if e == nil {
			svrcfg = &Service{}
			e := json.Unmarshal(b, svrcfg)
			if e != nil {
				svrcfg = nil
			}
			if svrcfg.Prefixs == nil {
				svrcfg = nil
			}
		}
	}
	if svrcfg == nil {
		svrcfg = &Service{
			Host:    "http://localhost:8080",
			Doc:     "http://localhost:8080/v2/api-docs",
			Prefixs: []string{"/api"},
		}
		save(svrcfg)
	}

	http.Handle("/", &Handle{
		fs: http.FileServer(
			&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: "html"}),
		cfg: svrcfg,
	})

	//http.Handle("/", &Handle{
	//	fs: http.FileServer(http.Dir("./admin/html")),
	//	cfg:svrcfg,
	//})
	listen := ":8080"
	if len(os.Args) >= 2 {
		listen = os.Args[1]
	}
	fmt.Println("start listen " + listen)
	//http.Handle("/", http.FileServer(assetFS()))
	http.ListenAndServe(listen, nil)

}
